#include "FrameQueue.h"
#include "VDPlayer.h"

extern "C"
{
#include <libavcodec/avcodec.h>
}

FrameQueue::FrameQueue(int maxSize)
{
	this->maxSize = maxSize;
	memset(frames, 0, sizeof(frames));
	for (int i = 0; i < maxSize; i++)
	{
		FrameNode* node = &frames[i];
		node->serial = -1;
		AVFrame_Alloc();
		node->frame = av_frame_alloc();
	}
	Init_Mutex(mutex);
	Init_Condition(condition);
}


FrameQueue::~FrameQueue()
{
	ClearQueue();
	for (int i = 0; i < maxSize; i++)
	{
		FrameNode* node = &frames[i];
		AVFrame_Free(&node->frame);
		node->pts = 0;
		node->serial = -1;
	}
	Destroy_Mutex(mutex);
	Destroy_Condition(condition);
}

void FrameQueue::Release()
{
	isExit = true;
	Cond_Signal_All(condition);
	printf("Frame Queue Release!\n");
}




int FrameQueue::Enqueue(AVFrame* frame, int64_t pts, int serial)
{
	if (isExit) return -1;
	Mutex_Lock(mutex);
	while (size >= maxSize && !isExit)
		Cond_Wait(condition, mutex);
		

	if (isExit)
	{
		Mutex_Unlock(mutex);
		return -1;
	}
	AVFrame_Alloc();
	av_frame_move_ref(frames[windex].frame, frame);
	frames[windex].pts = pts;
	frames[windex].serial = serial;
	if (++windex == maxSize) windex = 0;

	size++;
	Cond_Signal_One(condition);
	Mutex_Unlock(mutex);
	return 0;
}

FrameNode* FrameQueue::PeekLast()
{
	return &frames[rindex];
}

FrameNode* FrameQueue::Peek()
{
	int index = (rindex + rindex_shown) % maxSize;
	return &frames[index];
}

FrameNode* FrameQueue::PeekNext()
{
	int index = (rindex + rindex_shown + 1) % maxSize;
	return &frames[index];
}

void FrameQueue::QueueNextSafe()
{
	Mutex_Lock(mutex);
	QueueNext();
	Mutex_Unlock(mutex);
}


void FrameQueue::QueueNext()
{
	if (!rindex_shown)
	{
		rindex_shown = 1;
		return;
	}
	AVFrame_Free(NULL, true);
	av_frame_unref(frames[rindex].frame);
	if (++rindex == maxSize)
		rindex = 0;

	size--;
	Cond_Signal_One(condition);
}

int FrameQueue::QueueRemaining()
{
	return size - rindex_shown;
}

int FrameQueue::Denqueue(FrameNode* &node)
{
	if (isExit) return -1;
	Mutex_Lock(mutex);
	while (size - rindex_shown <= 0 && !isExit)
		Cond_Wait(condition, mutex);

	if (isExit) {
		Mutex_Unlock(mutex);
		return -1;
	}

	node = &frames[(rindex + rindex_shown) % maxSize];
	QueueNext();
	Mutex_Unlock(mutex);
	return 0;
}

void FrameQueue::ClearQueue()
{
	Mutex_Lock(mutex);
	while (QueueRemaining() >= 0)
		QueueNext();
	size = 0;
	Mutex_Unlock(mutex);
}

